package com.sailorj.itools.utils;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.sailorj.itools.constants.SailorjConfigConstants;
import com.sailorj.itools.entity.CommitLines;
import com.sailorj.itools.entity.CommitLogInfo;
import org.apache.commons.lang3.time.StopWatch;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.LogCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.*;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.patch.FileHeader;
import org.eclipse.jgit.patch.HunkHeader;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;
import java.util.List;
import java.util.concurrent.TimeUnit;


public class GitUtil {

    /**
     * 可优化成多线程分析数据，最终执行完毕整合
     * @param git
     * @param repository
     * @param projectName
     * @throws GitAPIException
     * @throws IOException
     */
    public static void gitUtil(Project project, Git git, Repository repository, String projectName) throws GitAPIException, IOException {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        // 2.执行git pull操作，获取其他人的操作信息
        // git.pull();
        ArrayList<CommitLogInfo> results = new ArrayList<>();

        // 3.执行git log查看日志信息，并进行过滤，只查询最近15天的所有的数据
        LogCommand logCommand = git.log();
        Iterable<RevCommit> call = logCommand.call();
        String branchName = git.getRepository().getBranch();

        ArrayList<RevCommit> arrayList = new ArrayList<>();
        for (RevCommit revCommit : call) {
            arrayList.add(revCommit);
        }
        int length = arrayList.size();
        Map<String, Map<String, Integer>> hashMap = new HashMap<>();
        for (int i = 0; i <= length - 2; i++) {
            RevCommit revCommit = arrayList.get(i);
            RevCommit prevCommit = arrayList.get(i + 1);
            String commitID = revCommit.getId().toString().split(" ")[1];
            String commitComment = revCommit.getFullMessage().replaceAll("'", "").replaceAll("\n", "\t");
            PersonIdent personIdent = revCommit.getCommitterIdent();
            String commitName = personIdent.getName();
            String commitEmail = personIdent.getEmailAddress();
            long commitTime = revCommit.getCommitTime() * 1000L;
            Timestamp runningTimestamp = new Timestamp(commitTime);
            // 获取变化的行数
            CommitLines commitLines = getCommitLines2(git, prevCommit, revCommit, repository);
            CommitLogInfo commitLogInfo = new CommitLogInfo();
            commitLogInfo.setProjectName(projectName); // 项目名
            commitLogInfo.setBranchName(branchName); // git分支名称
            commitLogInfo.setCommitID(commitID); // 提交id
            commitLogInfo.setCommitUserName(commitName); // 作者
            commitLogInfo.setCommitEmail(commitEmail); // 作者邮箱
            commitLogInfo.setCommitComment(commitComment); // 提交注释
            commitLogInfo.setCommitTime(runningTimestamp); // 提交时间
            commitLogInfo.setAddLines(commitLines.getAddLines()); // 新增行数
            commitLogInfo.setSubLines(commitLines.getSubLines()); // 删除行数

            Map<String, Integer> cnt = new HashMap<>();
            if (hashMap.containsKey(commitName)) {
                cnt.put("+", commitLines.getAddLines() + hashMap.get(commitName).get("+"));
                cnt.put("-", commitLines.getSubLines() + hashMap.get(commitName).get("-"));
            } else {
                cnt.put("+", commitLines.getAddLines());
                cnt.put("-", commitLines.getSubLines());
            }
            hashMap.put(commitName, cnt);
            results.add(commitLogInfo);
        }
        StringBuilder sb = new StringBuilder();
        stopWatch.stop();
        String msg = "读取分析.git数据成功！耗时:" + stopWatch.getTime(TimeUnit.SECONDS) + "s\n";
        sb.append(msg);
        Set<Map.Entry<String, Map<String, Integer>>> ms = hashMap.entrySet();
        for (Map.Entry entry : ms) {
            String sql_val = String.format("作者:%s, +总新增行数:%s, -总删除行数:%s\n",
                    entry.getKey(), ((Map) entry.getValue()).get("+"), ((Map) entry.getValue()).get("-"));
            sb.append(sql_val);
        }
        sb.append("\n以下是提交详情\n\n");
        for (CommitLogInfo commitLogInfo : results) {
            String sql_val = String.format("项目:%s, 作者:%s, 提交注释:%s, 提交时间:%s, 新增行数:%s, 删除行数:%s\n",
                    commitLogInfo.getProjectName(), commitLogInfo.getCommitUserName(),
                    commitLogInfo.getCommitComment(), commitLogInfo.getCommitTime(),
                    commitLogInfo.getAddLines(), commitLogInfo.getSubLines());
            sb.append(sql_val);
        }
        showPopupBalloon(project, sb.toString());
    }

    private static void showPopupBalloon(final Project project, final String msg) {
        ApplicationManager.getApplication().invokeLater(() -> {
            // 弹窗
            Messages.showMessageDialog(project, msg, SailorjConfigConstants.title, Messages.getInformationIcon());
        });
    }

    /**
     * @param git
     * @param headCommit
     * @param currentCommit
     * @param repository
     * @return
     * @throws GitAPIException
     * @throws IOException
     */
    public static CommitLines getCommitLines2(Git git, RevCommit headCommit, RevCommit currentCommit, Repository repository) throws GitAPIException, IOException {
        List<DiffEntry> diffs;

        AbstractTreeIterator oldTreeIter = prepareTreeParser(repository, headCommit);
        AbstractTreeIterator newTreeIter = prepareTreeParser(repository, currentCommit);
        diffs = git.diff().setNewTree(newTreeIter).setOldTree(oldTreeIter).call();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        DiffFormatter df = new DiffFormatter(out);
        df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
        df.setRepository(repository);
        int addLines = 0;
        int subLines = 0;
        for (DiffEntry diffEntry : diffs) {
            df.format(diffEntry);
            FileHeader fileHeader = df.toFileHeader(diffEntry);
            List<HunkHeader> hunks = (List<HunkHeader>) fileHeader.getHunks();
            int addSize = 0;
            int subSize = 0;
            for (HunkHeader hunkHeader : hunks) {
                EditList editList = hunkHeader.toEditList();
                for (Edit edit : editList) {
                    subSize = subSize + edit.getEndA() - edit.getBeginA();
                    addSize = addSize + edit.getEndB() - edit.getBeginB();
                }
            }
            out.reset();
            addLines += addSize;
            subLines += subSize;
        }
        return new CommitLines(addLines, subLines);

    }

    public static AbstractTreeIterator prepareTreeParser(Repository repository, RevCommit commit) {
        try (RevWalk walk = new RevWalk(repository)) {
            RevTree tree = walk.parseTree(commit.getTree().getId());

            CanonicalTreeParser oldTreeParser = new CanonicalTreeParser();
            try (ObjectReader oldReader = repository.newObjectReader()) {
                oldTreeParser.reset(oldReader, tree.getId());
            }

            walk.dispose();

            return oldTreeParser;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
